---
title: 権限管理
---

Univer はブック・シート・セル範囲に対する権限制御機能を提供します。ユーザーが許可されていない操作を実行しようとすると、コード実行を停止し、権限不足を通知できます。たとえば、範囲保護（エリア保護）を設定し、その範囲内で他の共同編集者が編集 / 閲覧 / コピー / フィルター等を行えるかどうかを制御できます。

**注意: Univer が提供するのは拡張可能な基盤能力であり、カスタマイズ済み機能ではありません。永続化や組織構造など、より広範かつ特化した要件がある場合は、権限ルールの保存および組織構造との統合を自ら実装する必要があります。その際はカスタムプラグインを作成してください。**
そのため、権限を設定した後に権限一覧が空であったり、ユーザー情報を取得しても空で返ることがあります。これは当該情報が API 経由で取得されるべきカスタムロジックであり、追加実装が必要だからです。以下の「サードパーティ認可サービス統合」を参照してください。

<PlaygroundFrame lang="en-US" slug="sheets/permission" clickToShow />

## 基本例

### ブック（Workbook）権限

現状、ブックレベルの権限は API を通じて直接変更します。Univer は [Facade API](/guides/sheets/getting-started/facade) を提供しており、これを用いてブックの各機能に対する権限を設定できます。

編集権限を例にします（他機能は末尾に列挙している権限ポイントへ置き換えてください。これらは `@univerjs/sheets` からインポートします）。次のコードでブック全体を編集不可にできます:

```typescript
import { FUniver } from '@univerjs/core/facade'

const univerAPI = FUniver.newAPI(univer)
```

次に、univerAPI のメソッドを利用して FPermission オブジェクトを取得し、個別権限を設定します:

```typescript
const permission = univerAPI.getActiveWorkbook().getPermission()
const workbookEditablePermission = permission.permissionPointsDefinition.WorkbookEditablePermission
// unitId はブック ID。WorkbookEditablePermission は権限ポイント。false は権限不可を意味する
permission.setWorkbookPermissionPoint(unitId, workbookEditablePermission, false)
```

### ワークシート権限コード例

ワークシートおよび範囲に関連する権限は Facade API とコマンド両方で設定可能です。ここではワークシート編集権限を例とします。その他のワークシート機能は末尾の権限ポイント一覧を参照し置き換えてください。

<Tabs items={['Facade API', 'コマンド方式']}>
  <Tab>
    ```typescript
    const permission = univerAPI.getActiveWorkbook().getPermission()
    const worksheetEditPermission = permission.permissionPointsDefinition.WorksheetEditPermission
    // ワークシート権限の生成。unitId はブック ID、subUnitId はシート ID。生成される permissionId は権限領域描画用の基本権限識別子
    const permissionId = await permission.addWorksheetBasePermission(unitId, subUnitId)

    permission.sheetRuleChangedAfterAuth$.subscribe((currentPermissionId) => {
      if (currentPermissionId === permissionId) {
        // ワークシートを編集不可に設定
        permission.setWorksheetPermissionPoint(unitId, subUnitId, worksheetEditPermission, false)
      }
    })
    ```
  </Tab>
  <Tab>
    ```typescript
    import { ICommandService, IPermissionService, IUniverInstanceService } from '@univerjs/core'
    import { AddWorksheetProtectionMutation, getSheetCommandTarget, WorksheetEditPermission } from '@univerjs/sheets'

    const accessor = univer.__getInjector()
    const commandService = accessor.get(ICommandService)
    const univerInstanceService = accessor.get(IUniverInstanceService)

    const target = getSheetCommandTarget(univerInstanceService)
    if (!target) {
      return
    }

    const { unitId, subUnitId } = target

    commandService.executeCommand(AddWorksheetProtectionMutation.id, {
      unitId,
      subUnitId,
      rule: {
        permissionId: '2sxcza1',
        name: 'sheet',
        unitType: 2,
        unitId,
        subUnitId,
      },
    })

    const permissionService = accessor.get(IPermissionService)
    permissionService.updatePermissionPoint(new WorksheetEditPermission(unitId, subUnitId).id, false)
    ```
  </Tab>
</Tabs>

このコマンドのパラメータ: `permissionId` は保存用に自分で生成する一意 ID、`unitType` はシート種別（リポジトリの UnitObject enum 利用可）、unitId はブック ID、`subUnitId` はシート ID。

#### ワークシート権限の削除

<Tabs items={['API メソッド', 'コマンド方式']}>
  <Tab>
    ```typescript
    const permission = univerAPI.getActiveWorkbook().getPermission()
    permission.removeWorksheetPermission(unitId, subUnitId)
    ```
  </Tab>
  <Tab>
    ```typescript
    import { DeleteWorksheetProtectionMutation } from '@univerjs/sheets' // [!code ++]

    commandService.executeCommand(DeleteWorksheetProtectionMutation.id, {
      unitId,
      subUnitId,
    })
    ```
  </Tab>
</Tabs>

### カスタム範囲権限コード例

範囲（Range）権限も API とコマンドの両方式で設定できます。ここでは範囲編集権限を例とします。他の範囲機能は末尾の一覧の権限ポイントへ置き換えてください。

<Tabs items={['API メソッド', 'コマンド方式']}>
  <Tab>
    ```typescript
    const workbook = univerAPI.getActiveWorkbook()
    const sheet = workbook.getActiveSheet()
    const unitId = workbook.getId()
    const subUnitId = sheet.getSheetId()

    const range1 = sheet.getRange('A1:B3')
    const range2 = sheet.getRange('C4:D5')
    const ranges = [range1, range2]

    const permission = univerAPI.getActiveWorkbook().getPermission()
    const rangeProtectionPermissionEditPoint = permission.permissionPointsDefinition.RangeProtectionPermissionEditPoint
    const res = await permission.addRangeBaseProtection(unitId, subUnitId, ranges)
    // レスポンスはワークシート権限と異なり、1 シートに複数範囲保護が存在し得るため ruleId が一意ルール保存用、permissionId は権限ポイント集合用
    const { permissionId, ruleId } = res

    permission.rangeRuleChangedAfterAuth$.subscribe((currentPermissionId) => {
      if (currentPermissionId === permissionId) {
        // 範囲保護を編集不可に設定
        permission.setRangeProtectionPermissionPoint(unitId, subUnitId, permissionId, rangeProtectionPermissionEditPoint, false)
      }
    })
    ```
  </Tab>
  <Tab>
    ```typescript
    import { ICommandService, IPermissionService, IUniverInstanceService } from '@univerjs/core'
    import { AddRangeProtectionMutation, getSheetCommandTarget, RangeProtectionPermissionEditPoint } from '@univerjs/sheets'

    const accessor = univer.__getInjector()
    const commandService = accessor.get(ICommandService)
    const univerInstanceService = accessor.get(IUniverInstanceService)

    const target = getSheetCommandTarget(univerInstanceService)
    if (!target) {
      return
    }

    const { unitId, subUnitId } = target
    const ranges = [
      {
        startRow: 0,
        startColumn: 0,
        endRow: 2,
        endColumn: 1,
      },
      {
        startRow: 3,
        startColumn: 2,
        endRow: 4,
        endColumn: 3,
      },
    ]

    commandService.executeCommand(AddRangeProtectionMutation.id, {
      unitId,
      subUnitId,
      rules: [{
        permissionId: '3xtfxG1',
        name: 'sheet1',
        unitType: 3,
        unitId,
        subUnitId,
        ranges,
        id: 'rule1',
      }],
    })

    const permissionService = accessor.get(IPermissionService)
    // RangeProtectionPermissionEditPoint の第3引数は生成した permissionId。false は編集不可
    permissionService.updatePermissionPoint(new RangeProtectionPermissionEditPoint(unitId, subUnitId, '3xtfxG1').id, false)
    ```
  </Tab>
</Tabs>

ここでのパラメータは前述とほぼ同じで、選択範囲を示す `ranges`、および削除時に利用する一意識別子 `id` が追加されています。

#### 範囲保護権限の削除

<Tabs items={['API メソッド', 'コマンド方式']}>
  <Tab>
    ```typescript
    const permission = univerAPI.getActiveWorkbook().getPermission()
    permission.removeRangeProtection(unitId, subUnitId, ruleIds)
    ```
  </Tab>
  <Tab>
    ```typescript
    import { DeleteRangeProtectionMutation } from '@univerjs/sheets' // [!code ++]

    commandService.executeCommand(DeleteRangeProtectionMutation.id, {
      unitId,
      subUnitId,
      ruleIds: ['rule1'],
    })
    ```
  </Tab>
</Tabs>

### 権限ダイアログの非表示

```typescript
const permission = univerAPI.getActiveWorkbook().getPermission()
permission.setPermissionDialogVisible(false)
```

## 拡張利用

ここでは `WorkbookEditablePermission` を例に、独自プラグイン内で権限検証を追加します。他のポイントも同様です。

```typescript
import { IPermissionService } from '@univerjs/core'
import { WorkbookEditablePermission } from '@univerjs/sheets'

class YourService {
  constructor(@IPermissionService private _permissionService: IPermissionService) {
  }

  setWorkbookNotEditable() {
    this._permissionService.updatePermissionPoint(new WorkbookEditablePermission('unitId').id, false)
  }

  setWorkbookEditable() {
    this._permissionService.updatePermissionPoint(new WorkbookEditablePermission('unitId').id, true)
  }
}
```

他の権限ポイントを拡張・変更して別機能の権限制御も可能です。具体的なポイント一覧は記事末尾を参照してください。

### 権限ポイントの拡張方法

```typescript
import type { IPermissionPoint } from '@univerjs/core'
import { IPermissionService } from '@univerjs/core'

export class CustomPermissionPoint implements IPermissionPoint {
  type = UnitObject.Unkonwn // 任意のタイプ
  subType = UnitAction.View // 任意のサブタイプ
  status = PermissionStatus.INIT
  value = true // 初期値
  id: string
  constructor(unitId: string, subUnitId: string, customId: string) {
    // id は IPermissionService 全体で一意である必要がある
    this.id = `${unitId}.${subUnitId}.${customId}`
  }
}

class YourService {
  constructor(@IPermissionService private _permissionService: IPermissionService) {
    this._init()
  }

  _init() {
    this._permissionService.addPermissionPoint(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id'))
  }
}

// 他所での利用例
class ConsumeService {
  constructor(@IPermissionService private _permissionService: IPermissionService) {
  }

  doSomething() {
    const point = this._permissionService.getPermissionPoint(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id').id)
    console.log(point.value)
  }

  bindEvent() {
    // これは Rx オブジェクトを取得し、権限変更を監視したりリストを更新したりできる
    const pount$ = this._permissionService.getPermissionPoint$(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id').id)
    console.log(pount$)
  }
}
```

### サードパーティ認可サービスの統合（高度な利用）

<Callout type="warning" title="注意事項">
  カスタム権限アクセスと Permission Facade API の混在は避けることを推奨します。後者は権限ポイントをより細粒度に制御できるためです。
</Callout>

権限判定ロジックは通常外部サービスで処理され、通信が発生します。フロントエンド SDK 実装ではこのロジックを処理するために [AuthzIoLocalService](https://github.com/dream-num/univer/blob/dev/packages/core/src/services/authz-io/authz-io-local.service.ts) を利用しています。

本番環境ではこの実装をバックエンドサービスに置き換える必要があります。フロントエンドは [IAuthzIoService](https://github.com/dream-num/univer/blob/dev/packages/core/src/services/authz-io/type.ts) インターフェースに基づき、実行時差し替え用のリクエスト関数を実装します。

![Permission](./permission/permission-en.png)

以下は 2 つの事前定義ロール（Owner / Reader）に対し、保護された範囲の追加と削除を行う簡易例です。
Owner は保護範囲の編集・閲覧が可能で、Reader は保護範囲内セルの内容を編集・閲覧できません。

```typescript
import type { Injector } from '@univerjs/core'
import type { IActionInfo, IAllowedRequest, IBatchAllowedResponse, ICollaborator, ICreateRequest, ICreateRequest_SelectRangeObject, IListPermPointRequest, IPermissionPoint, IPutCollaboratorsRequest, IUnitRoleKV, IUpdatePermPointRequest } from '@univerjs/protocol'
import { createDefaultUser, generateRandomId, IAuthzIoService, Inject, IResourceManagerService, isDevRole, Univer, UserManagerService } from '@univerjs/core'
import { ObjectScope, UnitAction, UnitObject, UnitRole, UniverType } from '@univerjs/protocol'

class YourAuthzService implements IAuthzIoService {
  private _permissionMap: Map<string, ICreateRequest_SelectRangeObject & { objectType: UnitObject }> = new Map([])

  constructor(
    @IResourceManagerService private _resourceManagerService: IResourceManagerService,
    @Inject(UserManagerService) private _userManagerService: UserManagerService,
  ) {
    this._initSnapshot()
    this._initDefaultUser()
  }

  private _initDefaultUser() {
    const currentUser = this._userManagerService.getCurrentUser()
    const currentUserIsValid = currentUser && currentUser.userID
    if (!currentUserIsValid) {
      this._userManagerService.setCurrentUser(createDefaultUser(UnitRole.Owner))
    }
  }

  private _getRole(type: UnitRole) {
    const user = this._userManagerService.getCurrentUser()
    if (!user) {
      return false
    }
    return isDevRole(user.userID, type)
  }

  private _initSnapshot() {
    this._resourceManagerService.registerPluginResource({
      toJson: (_unitId: string) => {
        const obj = [...this._permissionMap.keys()].reduce((r, k) => {
          const v = this._permissionMap.get(k)
          r[k] = v!
          return r
        }, {} as Record<string, ICreateRequest_SelectRangeObject & { objectType: UnitObject }>)
        return JSON.stringify(obj)
      },
      parseJson: (json: string) => {
        return JSON.parse(json)
      },
      pluginName: 'SHEET_AuthzIoMockService_PLUGIN',
      businesses: [UniverType.UNIVER_SHEET, UniverType.UNIVER_DOC, UniverType.UNIVER_SLIDE],
      onLoad: (_unitId, resource) => {
        for (const key in resource) {
          this._permissionMap.set(key, resource[key])
        }
      },
      onUnLoad: () => {
        this._permissionMap.clear()
      },
    })
  }

  async create(config: ICreateRequest): Promise<string> {
    const permissionId = generateRandomId(8)
    if (config.objectType === UnitObject.SelectRange && config.selectRangeObject) {
      this._permissionMap.set(permissionId, { ...config.selectRangeObject, objectType: config.objectType })
    }
    return permissionId
  }

  async batchAllowed(config: IAllowedRequest[]): Promise<IBatchAllowedResponse['objectActions']> {
    const selectionRangeConfig = config.filter(c => c.objectType === UnitObject.SelectRange)
    if (selectionRangeConfig.length) {
      const currentUser = this._userManagerService.getCurrentUser()
      const res = [] as IBatchAllowedResponse['objectActions']
      selectionRangeConfig.forEach((c) => {
        res.push({
          unitID: c.unitID,
          objectID: c.objectID,
          actions: c.actions.map((action) => {
            if (isDevRole(currentUser.userID, UnitRole.Owner)) {
              return { action, allowed: true }
            }
            return { action, allowed: false }
          }),
        })
      })
      return res
    }
    return Promise.resolve([])
  }

  async list(config: IListPermPointRequest): Promise <IPermissionPoint[]> {
    const result: IPermissionPoint[] = []
    config.objectIDs.forEach((objectID) => {
      const rule = this._permissionMap.get(objectID)
      if (rule) {
        const item = {
          objectID,
          unitID: config.unitID,
          objectType: rule!.objectType,
          name: rule!.name,
          shareOn: false,
          shareRole: UnitRole.Owner,
          shareScope: -1,
          scope: {
            read: ObjectScope.AllCollaborator,
            edit: ObjectScope.AllCollaborator,
          },
          creator: createDefaultUser(UnitRole.Owner),
          strategies: [
            {
              action: UnitAction.View,
              role: UnitRole.Owner,
            },
            {
              action: UnitAction.Edit,
              role: UnitRole.Owner,
            },
          ],
          actions: config.actions.map((a) => {
            return { action: a, allowed: this._getRole(UnitRole.Owner) }
          }),
        }
        result.push(item)
      }
    })
    return result
  }

  async listCollaborators(): Promise<ICollaborator[]> {
    // 既存の共同編集者を列挙
    return []
  }

  async allowed(_config: IAllowedRequest): Promise<IActionInfo[]> {
    // これは権限処理用のモックサービスのため実ロジックは書かない。空配列を返し、ユーザーが元々設定した権限を変更しない。
    // 永続化を行いたい場合はここを改修
    return Promise.resolve([])
  }

  async listRoles(): Promise<{ roles: IUnitRoleKV[], actions: UnitAction[] }> {
    return {
      roles: [],
      actions: [],
    }
  }

  async update(config: IUpdatePermPointRequest): Promise<void> {
    // ビット情報更新
  }

  async updateCollaborator(): Promise<void> {
    // 共同編集者情報更新
    return undefined
  }

  async createCollaborator(): Promise<void> {
    // 新規共同編集者作成
    return undefined
  }

  async deleteCollaborator(): Promise<void> {
    return undefined
  }

  async putCollaborators(config: IPutCollaboratorsRequest): Promise<void> {
    return undefined
  }
}

export class YourPlugin extends Plugin {
  constructor(
    _config: unknown,
    @Inject(Injector) protected override _injector: Injector,
  ) {
  }

  override onStarting(): void {
    this._injector.add([IAuthzIoService, { useClass: YourAuthzService }])
  }
}

// override オプションで [[IAuthzIoService, null]] を設定することで、組み込み IAuthzIoService を登録しないよう指示できる。
// これにより YourAuthzService が認可サービス実装として利用される。
const univer = new Univer({
  override: [[IAuthzIoService, null]],
})

univer.registerPlugin(YourPlugin)
```

## 権限ポイントビット一覧

対象コードは次の [code](https://github.com/dream-num/univer/tree/dev/packages/sheets/src/services/permission/permission-point) を参照してください。

ブック権限とワークシート／範囲権限が交差する場合、利用には全て true に設定されている必要があります。

### ブック（Workbook）権限

| Permission                           | 説明                             |
|--------------------------------------|----------------------------------|
| WorkbookEditablePermission           | 編集可能                        |
| WorkbookPrintPermission              | 印刷可能                        |
| WorkbookCommentPermission            | コメント可能                    |
| WorkbookViewPermission               | 閲覧可能                        |
| WorkbookCopyPermission               | コピー可能                      |
| WorkbookExportPermission             | エクスポート可能                |
| WorkbookManageCollaboratorPermission | 共同編集者管理可能              |
| WorkbookCreateSheetPermission        | ワークシート作成可能            |
| WorkbookDeleteSheetPermission        | ワークシート削除可能            |
| WorkbookRenameSheetPermission        | ワークシート名称変更可能        |
| WorkbookHideSheetPermission          | ワークシート非表示設定可能      |
| WorkbookDuplicateSheetPermission     | ワークシート複製可能            |
| WorkbookSharePermission              | 共有可能                        |
| WorkbookMoveSheetPermission          | ワークシート移動可能            |
| WorksheetViewHistoryPermission       | 履歴閲覧可能                    |
| WorksheetRecoverHistoryPermission    | 履歴復元可能                    |

### ワークシート権限

| Permission                          | 説明                       |
|-------------------------------------|----------------------------|
| WorksheetCopyPermission             | コピー可能                 |
| WorksheetDeleteColumnPermission     | 列削除可能                 |
| WorksheetDeleteRowPermission        | 行削除可能                 |
| WorksheetFilterPermission           | フィルタ使用可能           |
| WorksheetInsertColumnPermission     | 列挿入可能                 |
| WorksheetInsertHyperlinkPermission  | ハイパーリンク使用可能     |
| WorksheetInsertRowPermission        | 行挿入可能                 |
| WorksheetPivotTablePermission       | ピボットテーブル使用可能   |
| WorksheetSetCellStylePermission     | セルスタイル編集可能       |
| WorksheetSetCellValuePermission     | セル値編集可能             |
| WorksheetSetColumnStylePermission   | 列スタイル設定可能         |
| WorksheetSetRowStylePermission      | 行スタイル設定可能         |
| WorksheetSortPermission             | 並べ替え可能               |
| WorksheetViewPermission             | 閲覧可能                   |
| WorksheetEditPermission             | 編集可能                   |

### 範囲保護

| Permission                           | 説明                                  |
|--------------------------------------|---------------------------------------|
| RangeProtectionPermissionViewPoint   | 保護範囲の内容を閲覧可能             |
| RangeProtectionPermissionEditPoint   | 保護範囲を編集可能                   |

## 設定

### 権限背景シャドウの非表示

権限背景シャドウはデフォルト表示です。以下で非表示にできます:

<Tabs items={['プリセットモード', 'プラグインモード']}>
  <Tab>
    ```typescript
    createUniver({
      presets: [
        UniverSheetsCorePreset({
          sheets: {
            protectedRangeShadow: false,
          },
        }),
      ],
    })
    ```
  </Tab>
  <Tab>
    ```typescript
    univer.registerPlugin(UniverSheetsUIPlugin, {
      protectedRangeShadow: false,
    })
    ```
  </Tab>
</Tabs>

### カスタムユーザーコンポーネント

Univer の標準ユーザーコンポーネントは適応性に限界があります。より複雑なカスタムコンポーネントが必要な場合は以下の方法で作成できます。

<Callout>
  カスタムコンポーネントの作り方は [Custom Components](/guides/sheets/ui/components) を参照してください。
</Callout>

<Tabs items={['プリセットモード', 'プラグインモード']}>
  <Tab>
    ```typescript
    const { univer } = createUniver({
      presets: [
        UniverSheetsCorePreset({
          sheets: {
            protectedRangeUserSelector: {
            /**
             * カスタムコンポーネント。`IPermissionDetailUserPartProps` インターフェースを実装すること
             */
              component: CustomPermissionDetailUserPart,
              /**
               * コンポーネントのフレームワーク。正しく指定する必要あり
               */
              framework: 'react',
            },
          },
        }),
      ],
    })
    ```
  </Tab>
  <Tab>
    ```typescript
    univer.registerPlugin(UniverSheetsUIPlugin, {
      protectedRangeUserSelector: {
        /**
         * カスタムコンポーネント。`IPermissionDetailUserPartProps` を実装
         */
        component: CustomPermissionDetailUserPart,
        /**
         * 利用フレームワーク
         */
        framework: 'react',
      },
    })
    ```
  </Tab>
</Tabs>

カスタムユーザー設定を完了したら、後続利用のために `sheetPermissionUserManagerService` サービスの `_selectUserList` に同期してください。

<Tabs items={['プリセットモード', 'プラグインモード']}>
  <Tab>
    ```typescript
    import { SheetPermissionUserManagerService, useDependency } from '@univerjs/preset-sheets-core'

    const sheetPermissionUserManagerService = useDependency(SheetPermissionUserManagerService)
    // 具体的なデータ構造は下記参照：https://github.com/dream-num/univer/blob/b4d4cfa063c9e6d5a82d1fc6b05edc206a415252/packages/sheets-ui/src/services/permission/sheet-permission-user-list.service.ts#L57
    sheetPermissionUserManagerService.setSelectUserList([])
    ```
  </Tab>
  <Tab>
    ```typescript
    import { SheetPermissionUserManagerService } from '@univerjs/sheets-ui'
    import { useDependency } from '@univerjs/ui'

    const sheetPermissionUserManagerService = useDependency(SheetPermissionUserManagerService)
    // データ構造参考：https://github.com/dream-num/univer/blob/b4d4cfa063c9e6d5a82d1fc6b05edc206a415252/packages/sheets-ui/src/services/permission/sheet-permission-user-list.service.ts#L57
    sheetPermissionUserManagerService.setSelectUserList([])
    ```
  </Tab>
</Tabs>
